import sys
from PySide6.QtWidgets import (
    QApplication, QMainWindow, QWidget, QPushButton, QListWidget,
    QVBoxLayout, QHBoxLayout, QFileDialog, QTextEdit, QInputDialog, QStatusBar, QToolTip, QListWidgetItem,
    QMessageBox
)
from PySide6.QtCore import Qt
import csv  # 用于 CSV 文件读写
import os  # 用于文件和目录操作
import zipfile  # 用于 ZIP 文件操作
import comtypes.client  # 用于调用 Windows COM 对象 (如 Word 和 PowerPoint)
import ezdxf
import fitz  # PyMuPDF，用于 PDF 操作
import openpyxl  # 用于 Excel 文件处理
from PIL import Image  # 用于图片处理
from PyPDF2 import PdfReader, PdfWriter  # 用于 PDF 拆分和合并
from pdf2docx import Converter  # 用于 PDF 转 Word
import json
import xml.etree.ElementTree as ET
from moviepy.editor import VideoFileClip
from pydub import AudioSegment
import sqlite3
import pandas as pd
import tabula
from docx import Document
import matplotlib.pyplot as plt


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("多功能文件处理工具")
        self.setGeometry(100, 100, 800, 620)

        # 左侧功能列表
        self.function_list = QListWidget()
        self.function_list.setMouseTracking(True)  # 启用鼠标追踪
        self.function_list.setViewMode(QListWidget.ListMode)  # 列表模式
        self.function_list.setSpacing(4)  # 项目之间间隔
        self.add_functions_to_list()
        self.function_list.itemEntered.connect(self.show_tooltip)  # 悬停显示说明

        # 日志输出区域
        self.log_output = QTextEdit()
        self.log_output.setReadOnly(True)
        self.log_output.setPlaceholderText("操作日志将在此显示...")

        # 执行和清除日志按钮
        self.execute_button = QPushButton("执行")
        self.execute_button.clicked.connect(self.run_selected_function)
        self.clear_log_button = QPushButton("清除日志")
        self.clear_log_button.clicked.connect(self.log_output.clear)

        # 右侧按钮布局
        button_layout = QHBoxLayout()
        button_layout.addWidget(self.execute_button)
        button_layout.addWidget(self.clear_log_button)

        # 右侧整体布局：上方日志区域 + 下方按钮
        right_layout = QVBoxLayout()
        right_layout.addWidget(self.log_output, 6)  # 上方日志占比更大
        right_layout.addLayout(button_layout, 2)  # 下方按钮

        # 左右布局：左侧功能列表 + 右侧日志和按钮
        main_layout = QHBoxLayout()
        main_layout.addWidget(self.function_list, 4)  # 左侧占比4
        main_layout.addLayout(right_layout, 6)  # 右侧占比6

        # 设置中心窗口
        central_widget = QWidget()
        central_widget.setLayout(main_layout)
        self.setCentralWidget(central_widget)

        # 状态栏
        self.status_bar = QStatusBar()
        self.setStatusBar(self.status_bar)

    def add_functions_to_list(self):
        """将功能添加到列表，并设置文字居中对齐"""
        functions = [
            "PDF 转 Word", "拆分 PDF", "合并 PDF", "图片转 PDF", "PDF 转图片",
            "PDF 转 Excel", "Word 转 PDF", "Word 转 Excel", "Excel 转 CSV",
            "CSV 转 Excel", "PPT 转 PDF", "JSON 转 XML", "TXT 转 JSON",
            "CAD 转 PNG", "JSON 转 TXT", "XML 转 JSON", "CSV 转 SQLite",
            "SQLite 导出为 CSV", "视频处理", "音频处理", "图片格式转换", "ZIP 文件压缩"
        ]
        for func in functions:
            item = QListWidgetItem(func)
            item.setTextAlignment(Qt.AlignCenter)  # 居中对齐
            self.function_list.addItem(item)


    def show_tooltip(self, item):
        """在悬停时显示功能说明"""
        function_name = item.text()
        description = self.get_function_description(function_name)
        QToolTip.showText(self.cursor().pos(), description, self)


    def get_function_description(self, function_name):
        """返回对应功能的操作说明"""
        descriptions = {
            "PDF 转 Word": "将 PDF 文件转换为 Word 格式。",
            "拆分 PDF": "将一个 PDF 文件拆分为多个 PDF。",
            "合并 PDF": "将多个 PDF 文件合并为一个。",
            "图片转 PDF": "将多张图片转换为一个 PDF 文件。",
            "PDF 转图片": "将 PDF 文件的每一页导出为图片。",
            "PDF 转 Excel": "从 PDF 提取数据并保存为 Excel 文件。",
            "Word 转 PDF": "将 Word 文件转换为 PDF。",
            "Word 转 Excel": "将 Word 内容导出为 Excel 文件。",
            "Excel 转 CSV": "将 Excel 文件转换为 CSV 文件。",
            "CSV 转 Excel": "将 CSV 文件转换为 Excel 文件。",
            "PPT 转 PDF": "将 PPT 文件转换为 PDF。",
            "JSON 转 XML": "将 JSON 文件转换为 XML 格式。",
            "TXT 转 JSON": "将 TXT 文件内容转换为 JSON 格式。",
            "CAD 转 PNG": "将 CAD 文件转换为 PNG 图片。",
            "JSON 转 TXT": "将 JSON 数据转换为可读的文本格式。",
            "XML 转 JSON": "将 XML 文件转换为 JSON 格式。",
            "CSV 转 SQLite": "将 CSV 数据导入到 SQLite 数据库。",
            "SQLite 导出为 CSV": "将 SQLite 数据库表导出为 CSV 文件。",
            "视频处理": "转换视频文件格式或进行编辑。",
            "音频处理": "转换音频文件格式或进行编辑。",
            "图片格式转换": "将图片转换为其他格式。",
            "ZIP 文件压缩": "将多个文件压缩为 ZIP 文件。",
        }
        return descriptions.get(function_name, "未找到该功能的说明。")

    def run_selected_function(self):
        """根据用户选择运行对应功能"""
        current_item = self.function_list.currentItem()
        if current_item:
            function_name = current_item.text()
            function_map = {
                "PDF 转 Word": self.run_pdf_to_word,
                "拆分 PDF": self.run_split_pdf,
                "合并 PDF": self.run_merge_pdfs,
                "图片转 PDF": self.run_image_to_pdf,
                "PDF 转图片": self.run_pdf_to_image,
                "PDF 转 Excel": self.run_pdf_to_excel,
                "Word 转 PDF": self.run_docx_to_pdf,
                "Word 转 Excel": self.run_docx_to_excel,
                "Excel 转 CSV": self.run_excel_to_csv,
                "CSV 转 Excel": self.run_csv_to_excel,
                "PPT 转 PDF": self.run_ppt_to_pdf,
                "JSON 转 XML": self.run_json_to_xml,
                "TXT 转 JSON": self.run_txt_to_json,
                "CAD 转 PNG": self.run_cad_to_png,
                "JSON 转 TXT": self.run_json_to_txt,
                "XML 转 JSON": self.run_xml_to_json,
                "CSV 转 SQLite": self.run_csv_to_sqlite,
                "SQLite 导出为 CSV": self.run_sqlite_to_csv,
                "视频处理": self.run_video_convert,
                "音频处理": self.run_audio_convert,
                "图片格式转换": self.run_convert_image,
                "ZIP 文件压缩": self.run_zip_files,
            }

            # 执行对应的函数
            func = function_map.get(function_name)
            if func:
                func()
                self.status_bar.showMessage(f"{function_name} 执行完成", 5000)
            else:
                self.log_output.append(f"[错误] 无法找到 '{function_name}' 的执行函数。")

    # 各个功能的实现方法
    def run_pdf_to_word(self):
        self.log_output.append("[INFO] 开始转换 PDF 到 Word...")
        pdf_file, _ = QFileDialog.getOpenFileName(self, "选择 PDF 文件")
        word_file, _ = QFileDialog.getSaveFileName(self, "保存为 Word 文件")
        try:
            cv = Converter(pdf_file)
            cv.convert(word_file, start=0, end=None)
            cv.close()
            self.log_output.append(f"[成功] PDF 转换为 Word：{word_file}")
        except Exception as e:
            self.log_output.append(f"[失败] PDF 转 Word 失败：{e}")

    def run_split_pdf(self):
        pdf_file, _ = QFileDialog.getOpenFileName(self, "选择 PDF 文件")
        output_dir = QFileDialog.getExistingDirectory(self, "选择输出目录")
        try:
            reader = PdfReader(pdf_file)
            os.makedirs(output_dir, exist_ok=True)  # 创建输出目录（如不存在）
            for i in range(len(reader.pages)):
                writer = PdfWriter()
                writer.add_page(reader.pages[i])
                output_path = f"{output_dir}/page_{i + 1}.pdf"
                with open(output_path, "wb") as f:
                    writer.write(f)
            self.log_output.append(f"[成功] PDF 拆分完成，保存到：{output_dir}")
        except Exception as e:
            self.log_output.append(f"[失败] PDF 拆分失败：{e}")


    def run_merge_pdfs(self):
        files, _ = QFileDialog.getOpenFileNames(self, "选择多个 PDF 文件")
        output_file, _ = QFileDialog.getSaveFileName(self, "保存合并后的 PDF")
        try:
            writer = PdfWriter()
            for pdf in files:
                reader = PdfReader(pdf)
                for page in reader.pages:
                    writer.add_page(page)
            with open(output_file, "wb") as f:
                writer.write(f)
            self.log_output.append(f"[成功] PDF 合并完成：{output_file}")
        except Exception as e:
            self.log_output.append(f"[失败] PDF 合并失败：{e}")


    def run_image_to_pdf(self):
        image_files, _ = QFileDialog.getOpenFileNames(self, "选择图片文件")
        pdf_file, _ = QFileDialog.getSaveFileName(self, "保存为 PDF 文件")
        try:
            images = [Image.open(img).convert("RGB") for img in image_files]
            images[0].save(pdf_file, save_all=True, append_images=images[1:])
            self.log_output.append(f"[成功] 图片合并为 PDF：{pdf_file}")
        except Exception as e:
            self.log_output.append(f"[失败] 图片转 PDF 失败：{e}")


    def run_pdf_to_image(self):
        pdf_file, _ = QFileDialog.getOpenFileName(self, "选择 PDF 文件")
        output_dir = QFileDialog.getExistingDirectory(self, "选择输出目录")
        try:
            doc = fitz.open(pdf_file)
            os.makedirs(output_dir, exist_ok=True)  # 创建输出目录（如不存在）
            for i in range(doc.page_count):
                page = doc.load_page(i)
                pix = page.get_pixmap()
                pix.save(f"{output_dir}/page_{i + 1}.png")
            self.log_output.append(f"[成功] PDF 转图片完成，保存到：{output_dir}")
        except Exception as e:
            self.log_output.append(f"[失败] PDF 转图片失败：{e}")


    def run_docx_to_pdf(self):
        docx_file, _ = QFileDialog.getOpenFileName(self, "选择 Word 文件")
        pdf_file, _ = QFileDialog.getSaveFileName(self, "保存为 PDF 文件")
        try:
            word = comtypes.client.CreateObject("Word.Application")
            doc = word.Documents.Open(docx_file)
            doc.SaveAs(pdf_file, FileFormat=17)
            doc.Close()
            word.Quit()
            self.log_output.append(f"[成功] Word 转 PDF：{pdf_file}")
        except Exception as e:
            self.log_output.append(f"[失败] Word 转 PDF 失败：{e}")


    def run_docx_to_excel(self):
        docx_file, _ = QFileDialog.getOpenFileName(self, "选择 Word 文件")
        excel_file, _ = QFileDialog.getSaveFileName(self, "保存为 Excel 文件")
        try:
            doc = Document(docx_file)
            workbook = openpyxl.Workbook()
            sheet = workbook.active

            for table in doc.tables:
                for row in table.rows:
                    sheet.append([cell.text for cell in row.cells])

            workbook.save(excel_file)
            self.log_output.append(f"[成功] {docx_file} 已转换为 {excel_file}")
        except Exception as e:
            self.log_output.append(f"[失败] Word 转 Excel 失败：{e}")


    def run_excel_to_csv(self):
        excel_file, _ = QFileDialog.getOpenFileName(self, "选择 Excel 文件")
        csv_file, _ = QFileDialog.getSaveFileName(self, "保存为 CSV 文件")
        try:
            wb = openpyxl.load_workbook(excel_file)
            sheet = wb.active
            with open(csv_file, "w", newline="") as f:
                writer = csv.writer(f)
                for row in sheet.iter_rows(values_only=True):
                    writer.writerow(row)
            self.log_output.append(f"[成功] Excel 转 CSV：{csv_file}")
        except Exception as e:
            self.log_output.append(f"[失败] Excel 转 CSV 失败：{e}")


    def run_csv_to_excel(self):
        csv_file, _ = QFileDialog.getOpenFileName(self, "选择 CSV 文件")
        excel_file, _ = QFileDialog.getSaveFileName(self, "保存为 Excel 文件")
        try:
            df = pd.read_csv(csv_file)
            df.to_excel(excel_file, index=False)
            self.log_output.append(f"[成功] {csv_file} 已转换为 {excel_file}")
        except Exception as e:
            self.log_output.append(f"[失败] CSV 转 Excel 失败：{e}")


    def run_ppt_to_pdf(self):
        ppt_file, _ = QFileDialog.getOpenFileName(self, "选择 PPT 文件")
        pdf_file, _ = QFileDialog.getSaveFileName(self, "保存为 PDF 文件")
        try:
            powerpoint = comtypes.client.CreateObject("Powerpoint.Application")
            presentation = powerpoint.Presentations.Open(ppt_file)
            presentation.SaveAs(pdf_file, 32)
            presentation.Close()
            powerpoint.Quit()
            self.log_output.append(f"[成功] PPT 转 PDF：{pdf_file}")
        except Exception as e:
            self.log_output.append(f"[失败] PPT 转 PDF 失败：{e}")


    def run_json_to_xml(self):
        json_file, _ = QFileDialog.getOpenFileName(self, "选择 JSON 文件")
        xml_file, _ = QFileDialog.getSaveFileName(self, "保存为 XML 文件")
        try:
            with open(json_file, 'r') as f:
                data = json.load(f)
            root = ET.Element("root")
            for key, value in data.items():
                child = ET.SubElement(root, key)
                child.text = str(value)
            tree = ET.ElementTree(root)
            tree.write(xml_file)
            self.log_output.append(f"[成功] {json_file} 已转换为 {xml_file}")
        except Exception as e:
            self.log_output.append(f"[失败] JSON 转 XML 失败：{e}")


    def run_txt_to_json(self):
        txt_file, _ = QFileDialog.getOpenFileName(self, "选择 TXT 文件")
        json_file, _ = QFileDialog.getSaveFileName(self, "保存为 JSON 文件")
        try:
            with open(txt_file, 'r') as f:
                lines = f.readlines()
            data = {f"line_{i+1}": line.strip() for i, line in enumerate(lines)}
            with open(json_file, 'w') as f:
                json.dump(data, f, indent=4)
            self.log_output.append(f"[成功] {txt_file} 已转换为 {json_file}")
        except Exception as e:
            self.log_output.append(f"[失败] TXT 转 JSON 失败：{e}")


    def run_cad_to_png(self):
        cad_file, _ = QFileDialog.getOpenFileName(self, "选择 CAD 文件")
        png_file, _ = QFileDialog.getSaveFileName(self, "保存为 PNG 文件")
        try:
            doc = ezdxf.readfile(cad_file)
            msp = doc.modelspace()

            # 获取图形实体的数据（以列表形式存储）
            lines = []
            for line in msp.query('LINE'):
                start = line.dxf.start
                end = line.dxf.end
                lines.append([start.x, start.y, end.x, end.y])

            # 创建绘图
            plt.figure()
            for line in lines:
                plt.plot([line[0], line[2]], [line[1], line[3]], color='black')

            plt.axis('equal')  # 保持比例
            plt.axis('off')    # 不显示坐标轴
            plt.savefig(png_file, bbox_inches='tight', pad_inches=0)
            plt.close()
            self.log_output.append(f"[成功] CAD 文件 {cad_file} 转换为 PNG：{png_file}")
        except Exception as e:
            self.log_output.append(f"[失败] CAD 转 PNG 失败：{e}")


    def run_json_to_txt(self):
        json_file, _ = QFileDialog.getOpenFileName(self, "选择 JSON 文件")
        txt_file, _ = QFileDialog.getSaveFileName(self, "保存为 TXT 文件")
        if json_file and txt_file:
            try:
                with open(json_file, 'r') as f:
                    data = json.load(f)
                with open(txt_file, 'w') as f:
                    for key, value in data.items():
                        f.write(f"{key}: {value}\n")
                self.log_output.append(f"JSON 转 TXT 成功：{txt_file}")
            except Exception as e:
                self.log_output.append(f"[失败] JSON 转 TXT 失败：{e}")


    def run_xml_to_json(self):
        xml_file, _ = QFileDialog.getOpenFileName(self, "选择 XML 文件")
        json_file, _ = QFileDialog.getSaveFileName(self, "保存为 JSON 文件")
        if xml_file and json_file:
            try:
                tree = ET.parse(xml_file)
                root = tree.getroot()
                data = {child.tag: child.text for child in root}
                with open(json_file, 'w') as f:
                    json.dump(data, f, indent=4)
                self.log_output.append(f"XML 转 JSON 成功：{json_file}")
            except Exception as e:
                self.log_output.append(f"[失败] XML 转 JSON 失败：{e}")


    def run_csv_to_sqlite(self):
        csv_file, _ = QFileDialog.getOpenFileName(self, "选择 CSV 文件")
        db_file, _ = QFileDialog.getSaveFileName(self, "保存为 SQLite 数据库")
        if csv_file and db_file:
            try:
                conn = sqlite3.connect(db_file)
                df = pd.read_csv(csv_file)
                df.to_sql("data", conn, if_exists="replace", index=False)
                conn.close()
                self.log_output.append(f"CSV 导入 SQLite 成功：{db_file}")
            except Exception as e:
                self.log_output.append(f"[失败] CSV 导入数据库失败：{e}")


    def run_pdf_to_excel(self):
        pdf_file, _ = QFileDialog.getOpenFileName(self, "选择 PDF 文件")
        excel_file, _ = QFileDialog.getSaveFileName(self, "保存为 Excel 文件")
        if pdf_file and excel_file:
            try:
                tabula.convert_into(pdf_file, excel_file, output_format='xlsx', pages='all')
                self.log_output.append(f"PDF 转 Excel 成功：{excel_file}")
            except Exception as e:
                self.log_output.append(f"[失败] PDF 转 Excel 失败：{e}")


    def ask_user_for_size(self, parent, image_file):
        # 弹出对话框询问是否保持原图大小
        choice, ok = QInputDialog.getItem(
            parent,
            "图片大小",
            "是否输出为原图大小？",
            ["是", "否"],
            editable=False
        )
        if not ok:
            return None  # 用户取消操作

        if choice == "是" :
            return None  # 保持原图大小

        # 用户选择调整图片大小
        while True:
            size_str, ok = QInputDialog.getText(parent, "输入大小", "请输入图片大小 (MB)：")
            if not ok:
                return None  # 用户取消

            try:
                target_size_mb = float(size_str)
                if target_size_mb <= 0:
                    raise ValueError("大小必须为正数。")
                return target_size_mb * 1024 * 1024  # 转换为字节（MB -> B）
            except ValueError as e:
                QMessageBox.warning(parent, "无效输入", f"无效输入：{e}")


    def compress_image(self, img, input_file , output_file, target_size):
        current_size = img.size
        current_file_size = os.path.getsize(input_file)

        if current_file_size == 0:
            self.log_output.append("当前文件大小为0，无法进行调整。")
            return

        # 计算目标文件大小与当前文件大小的比例
        target_ratio = target_size / current_file_size
        quality = 100  # 初始质量

        while True:
            # 通过调整质量或长宽来达到目标大小
            img.save(output_file, quality=quality, optimize=True)
            output_size = os.path.getsize(output_file)

            if (output_size <= target_size and target_ratio < 1) or (output_size >= target_size and target_ratio > 1):
                break

            if output_size > target_size:  # 需要压缩
                quality -= 5  # 每次减少质量以减小文件大小
            else:  # 需要放大
                quality += 5  # 每次增加质量以增大文件大小

            if quality < 10 or quality > 100:  # 质量限制
                break

        self.log_output.append(f"[成功] 图片已调整为 {output_file}，大小为 {output_size / (1024 * 1024):.2f} MB")

    def run_convert_image(self):
        input_file, _ = QFileDialog.getOpenFileName(self, "选择图片文件")
        output_file, _ = QFileDialog.getSaveFileName(self, "保存为新格式的图片")
        try:
            with Image.open(input_file) as img:
                img = img.convert("RGB")  # 保证兼容性，例如 PNG → JPEG

                target_size = self.ask_user_for_size(None,input_file)
                if target_size:  # 如果用户选择调整大小
                    self.compress_image(img, input_file , output_file, target_size)
                else:  # 保持原图大小
                    img.save(output_file)
                    self.log_output.append(f"[成功] {input_file} 已转换为 {output_file}（保持原图大小）。")
        except Exception as e:
            self.log_output.append(f"[失败] 图片转换失败：{e}")


    def run_zip_files(self):
        files, _ = QFileDialog.getOpenFileNames(self, "选择要压缩的文件")
        zip_file, _ = QFileDialog.getSaveFileName(self, "保存为 ZIP 文件")
        try:
            with zipfile.ZipFile(zip_file, 'w') as zf:
                for file in files:
                    zf.write(file)
            self.log_output.append(f"[成功] 文件打包为 ZIP：{zip_file}")
        except Exception as e:
            self.log_output.append(f"[失败] ZIP 打包失败：{e}")


    def run_sqlite_to_csv(self):
        db_file, _ = QFileDialog.getOpenFileName(self, "选择 SQLite 数据库")
        table_name, ok = QInputDialog.getText(self, "输入表名", "请输入要导出的表名：")
        csv_file, _ = QFileDialog.getSaveFileName(self, "保存为 CSV 文件")
        try:
            conn = sqlite3.connect(db_file)
            df = pd.read_sql_query(f"SELECT * FROM {table_name}", conn)
            df.to_csv(csv_file, index=False)
            conn.close()
            self.log_output.append(f"[成功] {db_file} 的表 '{table_name}' 已导出为 {csv_file}")
        except Exception as e:
            self.log_output.append(f"[失败] 数据库导出 CSV 失败：{e}")


    def run_video_convert(self):
        video_file, _ = QFileDialog.getOpenFileName(self, "选择视频文件")
        output_file, _ = QFileDialog.getSaveFileName(self, "保存为新视频文件")
        try:
            clip = VideoFileClip(video_file)
            clip.write_videofile(output_file)
            self.log_output.append(f"[成功] 视频 {video_file} 已转换为 {output_file}")
        except Exception as e:
            self.log_output.append(f"[失败] 视频转换失败：{e}")


    def run_audio_convert(self):
        input_file, _ = QFileDialog.getOpenFileName(self, "选择音频文件")
        output_file, _ = QFileDialog.getSaveFileName(self, "保存为新音频文件")
        try:
            audio = AudioSegment.from_file(input_file)
            audio.export(output_file, format=output_file.split('.')[-1])
            self.log_output.append(f"[成功] 音频 {input_file} 已转换为 {output_file}")
        except Exception as e:
            self.log_output.append(f"[失败] 音频转换失败：{e}")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())
